package com.apes.scm.voucher.model;

import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.DefaultPersistListener;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.comment.Comment;
import com.apes.framework.jpa.generator.ApesGenerator;
import com.apes.framework.jpa.repository.CustomRepository;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.soe.annotations.State;
import com.apes.framework.plugin.soe.annotations.States;
import com.apes.framework.plugin.soe.metadata.BusinessType;
import com.apes.framework.plugin.soe.store.database.repository.BusinessTypeRepository;
import com.apes.framework.util.*;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.Cooperation;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.repository.CooperationRepository;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.service.PartyRoleService;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.VoucherType;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.springframework.util.StringUtils;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能：凭证
 *
 * @author xul
 * @create 2018-02-11 9:46
 */
@Getter
@Setter
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Entity
@EntityListeners(DefaultPersistListener.class)
@Table(name = Voucher.CONST_TABLE_NAME, indexes = {@Index(columnList = "origin")})
public class Voucher extends Model {
    public static final String CONST_TABLE_NAME = "acc_voucher";

    @Id
    @Column(name = "id", length = 32)
    @ApesGenerator(code = CONST_TABLE_NAME, name = "凭证订单", prefix = "YP%(company)%(yy)%(month)", padding = 5, useDateRange = true)
    @Comment("凭证单号")
    private String id;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id", referencedColumnName = "id")
    @Comment("业务公司编码")
    private PartyRole company;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id", referencedColumnName = "id", nullable = false)
    @Comment("部门编码")
    private Dept dept;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "clerk_id", referencedColumnName = "id")
    @Comment("业务员编码")
    private PartyRole clerk;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "contract_id", referencedColumnName = "id")
    @Comment("采购/销售合同编码")
    private Contract contract;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "partner_id", referencedColumnName = "id")
    @Comment("合作伙伴编码")
    private PartyRole partner; //（供应商或客户）

//    @NotNull
//    @ManyToOne(fetch = FetchType.LAZY)
//    @JoinColumn(name = "cooperation_id", referencedColumnName = "id", nullable = false)
//    private Cooperation cooperation; //合作方式

    /**
     * 库存方式
     * 1. 适用于：经销
     * IM_OWN: 自有
     * 2. 适用于：代销、联营
     * IM_CONSIGN: 寄售
     */
    @States(name = "库存方式", values = {
            @State(id = InventoryMode.IM_OWN, name = "自有"),
            @State(id = InventoryMode.IM_CONSIGN, name = "寄售")})
    @Column(name = "inventory_mode", nullable = false, length = 20)
    private String inventoryMode; //库存方式

    public void setInventoryMode(String inventoryMode) {
        if (LogicalUtil.notIn(inventoryMode, InventoryMode.IM_OWN, InventoryMode.IM_CONSIGN)) {
            throw new RuntimeException("库存方式输入错误！");
        }
        this.inventoryMode = inventoryMode;
    }

    /**
     * 采购核算方式
     * 1. 适用于：经销、代销
     * AM_PURCHASE: 按采购价核算
     * 2. 适用于：联营
     * AM_SALE: 按售价（或码价）核算
     */
    @States(name = "采购核算方式", values = {
            @State(id = AccountingMethod.AM_PURCHASE, name = "按采购价核算"),
            @State(id = AccountingMethod.AM_SALE, name = "按售价（或码价）核算"),
            @State(id = AccountingMethod.AM_REAL, name = "按实时定价核算")})
    @Column(name = "accounting_method", nullable = false, length = 20)
    private String accountingMethod;

    public void setAccountingMethod(String accountingMethod) {
        if (LogicalUtil.notIn(accountingMethod, AccountingMethod.AM_PURCHASE, AccountingMethod.AM_SALE, AccountingMethod.AM_REAL)) {
            throw new RuntimeException("核算方式输入错误！");
        }
        this.accountingMethod = accountingMethod;
    }

    @Column(name = "voucher_type", length = 30, nullable = false)
    @Comment("凭证类型")
    private String voucherType;

    @Column(name = "origin", length = 30)
    @Comment("源单据")
    private String origin;

    @Column(name = "origin_business_type", length = 32, nullable = false)
    @Comment("源单据类型")
    private String originBusinessType;

    @Column(name = "source", length = 30)
    @Comment("来源单据")
    private String source;

    @Column(name = "source_business_type", length = 32, nullable = false)
    @Comment("来源单据类型")
    private String sourceBusinessType;

    @Column(name = "invoiced")
    @Comment("是否产生帐单")
    private boolean invoiced = false;

    @Column(name = "amt_tax", nullable = false, columnDefinition = ApesConst.FIELD_MONEY_16_4_0)
    @Comment("税金")
    private double amtTax = 0;

    @Column(name = "amt_untaxed", nullable = false, columnDefinition = ApesConst.FIELD_MONEY_16_4_0)
    @Comment("不含税金额")
    private double amtUntaxed = 0;

    @Column(name = "amt_total", nullable = false, columnDefinition = ApesConst.FIELD_MONEY_16_4_0)
    @Comment("总计")
    private double amtTotal = 0;

    @Column(name = "loss_Quotation_fee_Amt", nullable = false, columnDefinition = ApesConst.FIELD_MONEY_16_4_0)
    @Comment("定损信息费合计")
    private double lossQuotationFeeAmt = 0;

    @States(name = "状态", values = {
            @State(id = "wait", name = "待评估"),
            @State(id = "done", name = "已完成"),
            @State(id = "cancel", name = "已取消")})
    @Column(name = "state", length = 10)
    private String state; //状态： ('wait', '待评估'), ('done', '已完成'), ('cancel', '已取消')

    @Column(name = "is_return", columnDefinition = ApesConst.FIELD_BOOLEAN_FALSE, nullable = false)
    @Comment("是否退回")
    private boolean returned = false;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "voucher_id", referencedColumnName = "id")
    @Comment("退货凭证单号（销售退货时使用）")
    private Voucher voucher;

    @Column(name = "discount", nullable = false, columnDefinition = ApesConst.FIELD_16_4_0)
    @Comment("折扣")
    private double discount = 0;

    @Column(name = "supply_chain_management_fee", nullable = false, columnDefinition = ApesConst.FIELD_16_4_0)
    @Comment("供应链管理费")
    private double supplyChainManagementFee = 0;

    @Column(name = "platform_management_fee", nullable = false, columnDefinition = ApesConst.FIELD_16_4_0)
    @Comment("平台管理费")
    private double platformManagementFee = 0;

    @Column(name = "platform_service_fee_reduced", nullable = false, columnDefinition = ApesConst.FIELD_16_4_0)
    @Comment("平台服务费减免额")
    private double platformServiceFeeReduced = 0;

    @Column(name = "source_supplier", length = 30)
    @Comment("原始供应商")
    private String sourceSupplier;

    @Column(name = "classification", length = 30)
    @Comment("凭证分类[01:直供;02:油漆商品;03:经销带安;04:代销带安;05:工单内协;06:整单内协;07:服务商品;08:委托定损;09:委托定损[定损方];" +
            "CROSS_IC:异业销售;CROSS_IC_CROSS_RETURN:异业退货[平台];CROSS_IC_RETURN:异业退货;E_MAINTAIN:e服务;INSURANCE:卖单定损]")
    private String classification;

    @OneToMany(mappedBy = "voucher", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<VoucherItem> voucherItems = new HashSet(); //凭证明细

    public Set<VoucherItem> getVoucherItems() {
        for (VoucherItem item : voucherItems) {
            item.setVoucher(this);
        }
        return voucherItems;
    }

    public void computeAmt() {
        for (VoucherItem item : voucherItems) {
            item.computeAmt();
            amtUntaxed += item.getPriceSubtotal();
            amtTax += item.getPriceTax();
            discount += item.getDiscount();
            supplyChainManagementFee += item.getSupplyChainManagementFee();
            platformManagementFee += item.getSupplyChainManagementFee();
            platformServiceFeeReduced += item.getPlatformServiceFeeReduced();
        }
        amtTotal = amtUntaxed + amtTax;
    }

    @Column(name = "business_type", length = 32)
    @Comment("业务单据类型")
    private String businessType; //业务单据类型

    public String getBusinessType() {
        if (!StringUtils.isEmpty(businessType)) {
            return businessType;
        }
        if (!StringUtils.isEmpty(originBusinessType)) {
            return originBusinessType;
        }
        throw new RuntimeException("业务单据类型不允许为空！");
    }

    @Column(name = "customer_expand_id", length = 32)
    @Comment("赋能客户车辆扩展信息")
    private String customerExpand; //业务单据类型

    @OneToMany(mappedBy = "voucher", fetch = FetchType.LAZY)
    private List<VoucherInventory> voucherInventories;

    @Transient
    private String originBusinessTypeName; //源单据类型

    public String getOriginBusinessTypeName() {
        return getBusinessTypeName("originBusinessType");
    }

    @Transient
    private String sourceBusinessTypeName; //来源单据类型

    public String getSourceBusinessTypeName() {
        return getBusinessTypeName("sourceBusinessType");
    }

    @Transient
    private String businessTypeName; //业务单据类型

    @Transient
    private double amtTaxCost = 0; //总含税成本金额

    @Transient
    private Date thirdTime;

    /**
     * 功能：计算总含税成本金额
     **/
    public double getAmtTaxCost() {
        if (voucherInventories == null) return amtTaxCost;
        for (VoucherInventory item : voucherInventories) {
            amtTaxCost += item.getTaxCostPrice() * item.getProductQty();
        }
        return Arith.round(amtTaxCost, 2);
    }

    public String getBusinessTypeName() {
        BusinessType type = SpringManager.getBean(BusinessTypeRepository.class).findOne(this.getBusinessType());
        if (type == null) {
            return null;
        }
        return type.getName();
    }

    @Transient
    public Cooperation checkCooperation() {
        Cooperation cooperation = null;
        if (accountingMethod != null && inventoryMode != null) {
            cooperation = SpringManager.getBean(CooperationRepository.class).findByInventoryModeAndAccountingMethod(inventoryMode, accountingMethod);
        }
        if (contract != null && cooperation == null) {
            cooperation = contract.getCooperation();
        }
        return cooperation;
    }

    /**
     * 发货部门（调拨使用）
     **/
    @Transient
    private Dept sourceDept;

    /**
     * 收货部门（调拨使用）
     **/
    @Transient
    private Dept receiptDept;

    /**
     * 平台公司
     **/
    @Transient
    private PartyRole platformCompany;

    public PartyRole getPlatformCompany() {
        if (platformCompany != null) return platformCompany;
        String topic = "event:fn.contract.findFirstValidFnContractCompanyByPartnerId";
        String platformCompanyId = (String) EventUtil.sendSynEvent(this, topic, MapUtil.mapper("partnerId", getCompany().getId())).stream().findFirst().get();
        if (!StringUtils.isEmpty(platformCompanyId)) {
            platformCompany = (PartyRole) JpaUtil.getImplementation(SpringManager.getBean(PartyRoleService.class).findOne(platformCompanyId, "Company"));
            platformCompany = MapUtil.getO(MapUtil.mapped(platformCompany), PartyRole.class);
        }
        return  platformCompany;
    }

    /**
     * 来源单据公司（异业）
     **/
    @Transient
    private PartyRole originCompany;

    public PartyRole getOriginCompany() {
        if (originCompany != null) return originCompany;
        if (getScene() == null || !("RO004".equals(getScene().getId()))) return null;
        if (StringUtils.isEmpty(getOriginBusinessType()) || StringUtils.isEmpty(getOrigin())) return null;
        Class clazz = JpaUtil.getDomainClass(getOriginBusinessType());
        if (clazz == null) return null;
        CustomRepository repository = JpaUtil.getRepository(clazz.getName());
        Object source = repository.findOne(JpaUtil.getId(getOriginBusinessType(), getOrigin()));
        Object company = ReflectUtil.getFieldValue(source, "company");
        if (company != null && company instanceof PartyRole) {
            originCompany = MapUtil.getO(MapUtil.mapped(JpaUtil.getImplementation(company)), PartyRole.class);
        }
        return originCompany;
    }
}
